<!--
 * @Description: 
 * @Autor: lot
 * @Date: 2024-01-30 11:23:24
 * @LastEditors: lot
 * @LastEditTime: 2024-01-30 15:40:14
-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>webgl基础概念-改进片段着色器</title>
    <style>
      body {
        margin: 0;
      }
      canvas {
        width: 100vw;
        height: 100vh;
        display: block;
      }
    </style>
  </head>
  <body>
    <canvas id="c"></canvas>

    <!-- 顶点着色器 -->
    <script id="vertex-shader-2d" type="notjs">
      //  将a_position改成vec2类型是因为我们只需要用x和y值。 vec2和vec4有些类似但是仅有x和y值。
      attribute vec2 a_position;

      //  添加了一个uniform(全局变量)叫做u_resolution,为了设置它的值我们需要找到它的位置。
      uniform vec2 u_resolution;

      // 所有的着色器都有一个main方法
      void main() {

        // 从像素坐标转换到 0.0 到 1.0
        vec2 zeroToOne = a_position / u_resolution;
        // 再把 0->1 转换 0->2
        vec2 zeroToTwo = zeroToOne * 2.0;
        // 把 0->2 转换到 -1->+1 (裁剪空间)
        vec2 clipSpace = zeroToTwo - 1.0;

        //翻转y轴
        gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
        // gl_Position = vec4(clipSpace, 0, 1);
      }
    </script>
    <!-- 片段着色器 -->
    <script id="fragment-shader-2d" type="notjs">
      // 片段着色器没有默认精度  这里设置了默认精度
      // 代表“medium precision” 中等精度
      precision mediump float;

      void main() {
        // gl_FragColor 是一个片段着色器主要设置的变量
        gl_FragColor = vec4(1, 0, 0.5, 1); // return redish-purple
      }
    </script>

    <script>
      // 创建着色器
      function createShader(gl, type, source) {
        var shader = gl.createShader(type);
        gl.shaderSource(shader, source);
        gl.compileShader(shader);
        var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
        if (success) {
          return shader;
        }

        console.log(gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
      }

      //   创建着色程序  link顶点着色器和片段着色器
      function createProgram(gl, vertexShader, fragmentShader) {
        var program = gl.createProgram();
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
        gl.linkProgram(program);
        var success = gl.getProgramParameter(program, gl.LINK_STATUS);
        if (success) {
          return program;
        }

        console.log(gl.getProgramInfoLog(program));
        gl.deleteProgram(program);
      }

      //   画布大小自适应
      function resizeCanvas() {
        // 获取浏览器中画布的显示尺寸
        var canvas = document.querySelector("#c");
        var displayWidth = canvas.clientWidth;
        var displayHeight = canvas.clientHeight;
        if (displayWidth != canvas.width || displayHeight != canvas.height) {
          canvas.height = displayHeight;
          canvas.width = displayWidth;
        }
        var gl = canvas.getContext("webgl");
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
      }

      function main() {
        // 获取webgl
        var canvas = document.querySelector("#c");
        var gl = canvas.getContext("webgl");
        if (!gl) {
          return;
        }

        // 获取着色器字符串
        var vertexShaderSource =
          document.querySelector("#vertex-shader-2d").text;
        var fragmentShaderSource = document.querySelector(
          "#fragment-shader-2d"
        ).text;

        // 创建顶点、片段着色器
        var vertexShader = createShader(
          gl,
          gl.VERTEX_SHADER,
          vertexShaderSource
        );
        var fragmentShader = createShader(
          gl,
          gl.FRAGMENT_SHADER,
          fragmentShaderSource
        );

        // link两个着色器生成着色程序
        var program = createProgram(gl, vertexShader, fragmentShader);

        // 从刚才创建的GLSL着色程序中找到顶点着色器中的变量，这是数据入口
        var positionAttributeLocation = gl.getAttribLocation(
          program,
          "a_position"
        );

        // 从程序中找到uniform变量
        var resolutionUniformLocation = gl.getUniformLocation(
          program,
          "u_resolution"
        );

        // 创建一个缓冲，因为属性  a_position  从缓冲中拿数据
        var positionBuffer = gl.createBuffer();

        // 绑定位置信息缓冲
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

        // 通过绑定点向缓冲中存放数据
        var positions = [10, 20, 80, 20, 10, 30, 10, 30, 80, 20, 80, 30];
        gl.bufferData(
          gl.ARRAY_BUFFER,
          //   WebGL需要强类型数据
          new Float32Array(positions),
          //   提示WebGL我们将怎么使用这些数据  gl.STATIC_DRAW提示WebGL不会经常改变这些数据
          gl.STATIC_DRAW
        );

        // 上方的是初始化代码。在页面加载时只会运行一次
        // 下方的是渲染代码。在我们每次要渲染或者绘制时执行
        resizeCanvas();
        // 为了裁剪空间坐标对应到画布像素坐标  传递画布的当前尺寸
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

        // 清空画布
        gl.clearColor(0, 0, 0, 0);
        gl.clear(gl.COLOR_BUFFER_BIT);

        // 运行着色程序
        gl.useProgram(program);

        // 设置全局变量
        gl.uniform2f(
          resolutionUniformLocation,
          gl.canvas.width,
          gl.canvas.height
        );

        // 启用对应属性
        gl.enableVertexAttribArray(positionAttributeLocation);

        // 将绑定点绑定到缓冲数据
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

        // 告诉属性怎么从positionBuffer中读取数据 (ARRAY_BUFFER)
        var size = 2; //每次迭代运行提取两个单位数据
        var type = gl.FLOAT; // 每个单位的数据类型是32位浮点型
        var normalize = false; // 不需要归一化数据
        var stride = 0; // 0 = 移动单位数量 * 每个单位占用内存（sizeof(type)）  每次迭代运行运动多少内存到下一个数据开始点
        var offset = 0; // 从缓冲起始位置开始读取
        gl.vertexAttribPointer(
          positionAttributeLocation,
          size,
          type,
          normalize,
          stride,
          offset
        );

        // draw
        var primitiveType = gl.TRIANGLES; //设置primitiveType（图元类型）为 gl.TRIANGLES（三角形）
        var offset = 0;
        var count = 6;
        gl.drawArrays(primitiveType, offset, count);

        /*
           裁剪空间          屏幕空间
            0, 0       ->   200, 150
            0, 0.5     ->   200, 225
            0.7, 0     ->   340, 150
        */
      }
      main();
    </script>
  </body>
</html>
